package com.insight.config.shiro.filters;

import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONObject;
import com.yuanqiao.insight.common.util.common.RedisUtils;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import com.insight.common.constant.CommonConstant;
import com.insight.config.shiro.oauth2.ISsoLoginService;
import com.insight.config.shiro.token.OAuth2Token;
import com.insight.config.shiro.utils.ShiroUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

/**
 * OAuth2认证过滤器，支持SSO单点登录、Token校验、跨域等功能。
 */
@Slf4j
@NoArgsConstructor
public class OAuth2Filter extends BasicHttpAuthenticationFilter {

    /**
     * SSO登录服务
     */
    private final ISsoLoginService ssoLoginService = SpringUtil.getBean("ssoLoginServiceImpl");
    /**
     * Redis工具类
     */
    private final RedisUtils redisUtil = SpringUtil.getBean(RedisUtils.class);

    /**
     * 是否允许跨域
     */
    private boolean allowOrigin = true;

    /**
     * 构造方法
     * @param allowOrigin 是否允许跨域
     */
    public OAuth2Filter(boolean allowOrigin) {
        this.allowOrigin = allowOrigin;
    }

    /**
     * 判断是否允许访问，校验Token有效性
     * @param request 请求对象
     * @param response 响应对象
     * @param mappedValue 映射值
     * @return 是否允许访问
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        String ssoToken = null;
        try {
            if (request instanceof HttpServletRequest) {
                ssoToken = ((HttpServletRequest) request).getHeader(CommonConstant.X_ACCESS_TOKEN);
            }
            if (log.isDebugEnabled() && request != null) {
                log.debug(ShiroUtils.getRequestUri(request));
            }
            return executeLogin(request, response);
        } catch (Exception e) {
            if (ssoToken != null) {
                redisUtil.del(CommonConstant.PREFIX_USER_TOKEN + ssoToken);
            }
            String uri = request != null ? ShiroUtils.getRequestUri(request) : "unknown";
            throw new AuthenticationException(String.format("Token失效，请重新登录。%s", uri), e);
        }
    }

    /**
     * 执行登录逻辑，自动创建用户并登录
     * @param request 请求对象
     * @param response 响应对象
     * @return 是否登录成功
     */
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) {
        if (request == null || !(request instanceof HttpServletRequest)) {
            throw new IllegalArgumentException("无效的 ServletRequest");
        }
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String ssoToken = httpServletRequest.getHeader(CommonConstant.X_ACCESS_TOKEN);
        String platformType = httpServletRequest.getHeader(CommonConstant.PLATFORM_TYPE);
        if (ssoToken == null) {
            throw new AuthenticationException("缺少X_ACCESS_TOKEN标题”);
        }
        boolean b = redisUtil != null && redisUtil.hasKey(CommonConstant.PREFIX_USER_TOKEN + ssoToken);
        if (!b) {
            try {
                JSONObject jwtPayload = ShiroUtils.decodeJwt(ssoToken);
                if (jwtPayload == null) {
                    throw new AuthenticationException("JWT 令牌无效");
                }
                jwtPayload.set(CommonConstant.PLATFORM_TYPE, platformType);
                String username = jwtPayload.getStr("user_name");
                if (username == null) {
                    throw new AuthenticationException("在令牌中找不到用户名");
                }
                Object user = ssoLoginService != null ? ssoLoginService.findUserByUsername(username) : null;
                if (user == null && ssoLoginService != null) {
                    ssoLoginService.createUser(ssoToken, jwtPayload);
                }
                if (ssoLoginService != null) {
                    ssoLoginService.ssoUserLogin(ssoToken, jwtPayload);
                }
            } catch (Exception e) {
                throw new AuthenticationException("过滤器逻辑出错", e);
            }
        }
        OAuth2Token oAuth2Token = new OAuth2Token(ssoToken, platformType);
        getSubject(request, response).login(oAuth2Token);
        return true;
    }

    /**
     * 对跨域提供支持
     * @param request 请求对象
     * @param response 响应对象
     * @return 是否继续后续处理
     * @throws Exception 处理异常
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) {
        try {
            if (ShiroUtils.extracted(request, response, allowOrigin)) {
                return false;
            }
            return super.preHandle(request, response);
        } catch (Exception e) {
            log.error("ShiroOriginFilter.preHandle 执行过程中发生未预期异常", e);
            return false;
        }
    }
}
